#ifndef GAME_H
#define GAME_H



#include "init.h"
#include "players.h"
#include "units.h"
#include <vector>
#include <QObject>



class object;

class player;

class game_state;



//! @brief Main map class - contains terrain and objects
class mapa
{

public:

	/**	@brief	Function for obaining the singleton
	*	@return	pointer to the singleton
	*/
	static mapa* get_instance();
	
	/** @brief Sets object do the map
	*	@param o object to set
	*	@param	x x coordinate, here to palce the object, constraint: 0 <= x < state->map->size_x
	*	@param	y y coordinate, here to palce the object, constraint: 0 <= y < state->map->size_y
	*/
	void set_object( object* o, unsigned int x, unsigned int y );
	/** @brief	Gets object from map
	*	@param	x x coordinate, from where to get the object, constraint: 0 <= x < state->map->size_x
	*	@param	y y coordinate, from where to get the object, constraint: 0 <= y < state->map->size_y
	*	@return	pointer to the object
	*/
	object* get_object( unsigned int x, unsigned int y );

	/** @brief	Loads map terrain from file
	*	@param	file file, where the map is stored
	*	first line contains size_x size_y, other lines terrain information
	*	10 - 100: terrain
	*	20000: water
	*	30000: wall
	*	number % 10: beginning place of player with this number
	*	@return	true, if loading is successful, else false
	*/
	bool load( const char* file );
	/** @brief	Loads map objects from file
	*	@param	file file, where the objects are stored
	*	file structure: "Type	Name	Pos_x	Pos_y	Health	Player_id"
	*	#: comment
	*	@return	true, if loading is successful, else false
	*/
	bool load_objects( const char* file );

	/** @brief Saves current objects to file
	*	@param file filename, where to save the objects
	*/
	void save_objects( const char* file );

	/** @brief	Creates clone of the terrain
	*	@return	pointer to the cloned array
	*/
	array2d< unsigned int >* terrain_clone();

	/**	@brief Saves 2D array of numbers to file
	*	@param map 2D array of numbers to save, constraint: map != 0
	*	@param file filename where to save the numbers
	*	@param sx x size of the array, constraint: sx <= map->s_x
	*	@param sy y size of the array, constraint: sy <= map->s_y
	*/
	static void export_map( array2d< unsigned int >* map, const char* file, unsigned int sx, unsigned int sy );

	/** @brief	Gets x size of the map
	*	@return	x size
	*/
	unsigned int get_size_x() { return size_x; };
	/** @brief	Gets y size of the map
	*	@return	y size
	*/
	unsigned int get_size_y() { return size_y; };
	
	//! 2D array of objects
	array2d< object* >* objects;
	//! 2D array of numbers representating map terrain
	array2d< unsigned int >* terrain;

	int x;


private:

	mapa( int _x ):x( _x ){};
	mapa( const mapa& );
	mapa& operator=( const mapa& x );

	/** @brief	Parses lines when loading objects from file
	*	@param	ol line to parse
	*	@return	true if parsing successful, else false
	*/
	bool parse_objects_line( std::string ol );

	//! x size
	unsigned int size_x;
	//! y size
	unsigned int size_y;

};



//! Class containing array of players
class players
{

public:

	/**	@brief	Function for obaining the singleton
	*	@return	pointer to the singleton
	*/
	static players* get_instance();

	//! @brief Fills the players array with imaginary local unnamed players with id -1
	void fill_players();

	/** @brief Sets player to specified position in the array and handles loaded units
	*	@param p player to set, constraint: p != 0
	*	@param x place where to set him, constraint: x <= "MAX_PL"
	*/
	void set_player( player* p, unsigned int x );
	/** @brief	Gets player from specified place in the array
	*	@param	x place from where to get the player, constraint: x <= "MAX_PL"
	*	@return pointer to the player
	*/
	player* get_player( unsigned int x );
	/** @brief	Gets number of players
	*	@return	Number of players
	*/
	unsigned int get_players_count() { return players_count; };
	/** @brief sets number of players
	*	@param c number of players to set
	*/
	void set_players_count( unsigned int c ) { players_count = c; };
	
	//! array of the players, initial size: "MAX_PL"
	player** pl;

	int x;


private:

	players( int _x ): x( _x )
	{
		pl = new player*[ parameters::get_instance()->game_parameters[ "MAX_PL" ] ];
		players_count = 0;
	};
	players( const players& );
	players& operator=( const players& x );

	//! number of players
	unsigned int players_count;
	
};



//! @brief Main class, that takes care of the whole game logics
class game: public QObject
{

	Q_OBJECT

public:

	/**	@brief	Function for obaining the singleton
	*	@return	pointer to the singleton
	*/
	static game* get_instance();

	/** @brief	Creates command from given parameters
	*	@param	c commant type
	*	@param	u unit number
	*	@return	generated command
	*/
	static std::string create_command( std::string c, unsigned int u );
	/** @brief	Creates command from given parameters
	*	@param	c commant type
	*	@param	u unit number
	*	@param	x x coordinate
	*	@param	y y coordinate
	*	@return	generated command
	*/
	static std::string create_command( std::string c, unsigned int u, unsigned int x, unsigned int y );

	//! @brief Forces AI player to send next command in "ai_delay" time
	void get_next_ai_command();
	//! @brief Forces AI player to send next command now
	void get_next_ai_command_now();

	//! @brief Sets beginning player, sets START_TIME, creates beginning game_state and saves beginning score
	void start_game();
	//! @brief Gets next player, sets START_TIME, sets game_state not up to date, saves score
	void next_turn();
	/** @brief	Tests if there are at least two players playing
	*	@return	pointer to the winner and saves score or 0 if game continues
	*/
	player* test_game_end();

	/** @brief	Gets current player
	*	@return	pointer to the current player
	*/
	player* get_current_player();
	/** @brief Sets player with specified place in palyers array
	*	@param n place of the player
	*/
	void set_current_player( unsigned int n );
	
	/** @brief	Tests if player can play (local && on turn)
	*	@param	p palyer to test
	*	@return true if can, else false
	*/
	bool can_play( const player* p );

	/** @brief	Checks if the game is paused
	*	@return	true, if it is, else false
	*/
	bool is_paused() { return ai_paused; };
	/** @brief Pauses/unpauses game
	*	@param paused true == pause, false == unpause
	*/
	void set_paused( bool paused );


	/** @brief	Gets current game state
	*	@return	current game state
	*/
	game_state* get_current_state();

	
	/** @brief Saves score to history of scores
	*	@param state game state from which to save score, constraint: state != 0
	*/
	void set_score_to_history( game_state* state );
	/** @brief	Gets game score history
	*	@return	matrix of scores
	*/
	std::vector< std::vector< double > >* get_score_history();


	int x;


private:

	game( int _x ): x( _x )
	{
		current_player = players::get_instance()->get_player( 0 );
		ai_paused = true;
		score_history = new std::vector< std::vector< double > >( players::get_instance()->get_players_count() );
	}
	game( const game& );
	game& operator=( const game& x );

	//!pointer co the current_player, initially first player from the players array
	player* current_player;

	//! status of ai, initially paused
	bool ai_paused;

	//! current game state
	game_state* current_state;
	//! if current game state is up to date
	bool up_to_date;

	//! matrix of game scores
	std::vector< std::vector< double > >* score_history;


public slots:

	//! @brief Slot for receiving commands from players
	void command_received( player* pl, std::string command );

};



#endif